package com.app.backend.activiti.controller;

import cn.hutool.core.collection.CollectionUtil;
import com.app.backend.activiti.entity.ActNode;
import com.app.backend.activiti.entity.ActProcess;
import com.app.backend.activiti.service.ActBusinessService;
import com.app.backend.activiti.service.ActNodeService;
import com.app.backend.activiti.service.ActProcessService;
import com.app.backend.activiti.vo.ProcessNodeVo;
import com.app.backend.common.constant.ActConstant;
import com.app.backend.common.response.BaseResponse;
import com.app.backend.common.response.ResponseCode;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.activiti.bpmn.model.Process;
import org.activiti.bpmn.model.*;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.ProcessDefinition;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;

/**
 * @program: ProcessController
 * @description:
 * @author: Zhun.Xiao
 * @create: 2020-02-27 10:43
 **/
@Slf4j
@RestController
@Api("流程定义管理")
@RequestMapping("/api/v1/act/process")
@Transactional
public class ActProcessController {

    @Autowired
    RepositoryService repositoryService;

    @Autowired
    ActProcessService actProcessService;


    @Autowired
    ActNodeService actNodeService;


    @Autowired
    private ActBusinessService actBusinessService;


    @PostMapping(value = "/getProcess")
    @ApiOperation("根据key获取最新的流程")
    public BaseResponse getLatestProcess(@RequestParam String key) {
        ActProcess process = actProcessService.findByProcessKeyAndLatest(key, true);
        return new BaseResponse(process);
    }


    @PostMapping(value = "/changeStatus")
    @ApiOperation(value = "激活or挂起流程")
    public BaseResponse changeStatus(@RequestParam String id, @RequestParam Integer status) {
        if (status.equals(ActConstant.PROCESS_STATUS_ACTIVE)) {
            repositoryService.activateProcessDefinitionById(id, true, new Date());
        } else if (status.equals(ActConstant.PROCESS_STATUS_SUSPEND)) {
            repositoryService.suspendProcessDefinitionById(id, true, new Date());
        }
        ActProcess actProcess = actProcessService.get(id);
        actProcess.setStatus(status);
        actProcessService.update(actProcess);
        return new BaseResponse("修改成功");
    }


    @PostMapping(value = "/getProcessNodes")
    @ApiOperation("通过流程定义di获取所有流程节点")
    public BaseResponse getProcessNodes(@ApiParam("流程定义id") @RequestParam String id) {

        BpmnModel bpmnModel = repositoryService.getBpmnModel(id);
        List<ProcessNodeVo> list = new ArrayList<>();
        List<Process> processes = bpmnModel.getProcesses();
        if (CollectionUtil.isEmpty(processes)) {
            return new BaseResponse();
        }
        for (Process process : processes) {
            Collection<FlowElement> flowElements = process.getFlowElements();
            for (FlowElement flowElement : flowElements) {
                ProcessNodeVo node = new ProcessNodeVo();
                node.setId(flowElement.getId());
                node.setTitle(flowElement.getName());
                if (flowElement instanceof StartEvent) {
                    node.setType(ActConstant.NODE_TYPE_START);
                } else if (flowElement instanceof UserTask) {
                    node.setType(ActConstant.NODE_TYPE_TASK);
                    node.setUsers(actNodeService.findUserByNodeId(flowElement.getId()));
                    node.setRoles(actNodeService.findRoleByNodeId(flowElement.getId()));
                } else if (flowElement instanceof EndEvent) {
                    node.setType(ActConstant.NODE_TYPE_END);
                } else {
                    continue;
                }

                list.add(node);
            }

        }
        return new BaseResponse(list);

    }


    @PostMapping(value = "/bindUsers")
    @ApiOperation(value = "为当前节点分配用户")
    public BaseResponse bindUsers(@RequestParam String nodeId,
                                  @RequestParam(required = false) String[] userIds,
                                  @RequestParam(required = false) String[] roleIds) {

        actNodeService.deleteByNodeId(nodeId);
        for (String userId : userIds) {
            ActNode actNode = new ActNode();
            actNode.setNodeId(nodeId);
            actNode.setRelateId(userId);
            actNode.setType(ActConstant.NODE_BIND_USER);
            actNodeService.save(actNode);
        }

        for (String roleId : roleIds) {
            ActNode actNode = new ActNode();
            actNode.setNodeId(nodeId);
            actNode.setRelateId(roleId);
            actNode.setType(ActConstant.NODE_BIND_ROLE);
            actNodeService.save(actNode);
        }
        return new BaseResponse();

    }


    @PostMapping(value = "/delProcess")
    @ApiOperation("删除流程（已有执行中的则无法删除）")
    public BaseResponse deleteProcessByIds(@RequestParam String[] ids) {

        for (String id : ids) {
            if (CollectionUtil.isNotEmpty(actBusinessService.findByProcDefId(id))) {
                return new BaseResponse(ResponseCode.SYSTEM_ERROR, "包含已在执行中的流程实例，无法删除");
            }
            ActProcess actProcess = actProcessService.get(id);
            if (actProcess.getVersion() == 1) {
                deleteNodeUsers(id);
            }
            repositoryService.deleteDeployment(actProcess.getDeploymentId());
            actProcessService.deleteById(id);
            actProcessService.setLatestByProcessKey(actProcess.getProcessKey());
        }
        return new BaseResponse("流程删除成功");

    }

    private void deleteNodeUsers(String id) {
        BpmnModel bpmnModel = repositoryService.getBpmnModel(id);
        List<Process> processes = bpmnModel.getProcesses();
        for (Process process : processes) {
            Collection<FlowElement> flowElements = process.getFlowElements();
            for (FlowElement el : flowElements) {
                actNodeService.deleteByNodeId(el.getId());
            }
        }
    }


    @RequestMapping(value = "/updateInfo", method = RequestMethod.POST)
    @ApiOperation(value = "修改/绑定 所属业务表及路由")
    public BaseResponse updateInfo(ActProcess actProcess) {
        ProcessDefinition pd = repositoryService.getProcessDefinition(actProcess.getId());
        if (pd == null) {
            return new BaseResponse(ResponseCode.PARAM_FAIL, "流程定义不存在");
        }
        actProcessService.update(actProcess);
        return new BaseResponse();
    }

    @PostMapping(value = "/export")
    @ApiOperation(value = "导出部署流程资源(图片/xml)")
    public void exportResource(@ApiParam("流程定义id") @RequestParam String id,
                               @ApiParam("0-xml,1-图片") @RequestParam Integer type,
                               HttpServletResponse response) throws Exception {
        ProcessDefinition pd = repositoryService.createProcessDefinitionQuery().processDefinitionId(id).singleResult();

        String resourceName = null;
        if (type.equals(ActConstant.RESOURCE_TYPE_XML)) {
            resourceName = pd.getResourceName();
        } else if (type.equals(ActConstant.RESOURCE_TYPE_IMAGE)) {
            resourceName = pd.getDiagramResourceName();
        } else {
            log.error("类型错误");
            throw new Exception("类型错误");
        }

        InputStream inputStream = repositoryService.getResourceAsStream(pd.getDeploymentId(), resourceName);

        try {
            response.setContentType("application/octet-stream;charset=UTF-8");
            response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(resourceName, "UTF-8"));
            byte[] b = new byte[1024];
            int len = -1;
            while ((len = inputStream.read(b, 0, 1024)) != -1) {
                response.getOutputStream().write(b, 0, len);
            }
            response.flushBuffer();
        } catch (IOException e) {
            log.error(e.toString());
            throw new Exception("导出部署流程资源失败");
        }


    }

}